import { createPromise } from "./promise";

function compareList<T>(a: T | any, key: string, value: any) {
    if (!a || !a.hasOwnProperty(key)) return -1;
    if (a[key] > value) return 1;
    if (a[key] == value) return 0;
    return -1;
}

/**
 * 开始二分法查找
 * @param list 查找用的列表
 * @param key 查找的单位元素
 * @param value 比较用的数值
 */
function orderListFind<T>(list: Array<T>, key: string, value: any, desc: boolean = false) {
    if (list.length == 0) return 0;
    var small = -1, big = list.length;
    while (true) {
        var ret = 0;
        var center = Math.floor((small + big) / 2);
        if (small == big) {
            if (small == -1 || big == list.length) return small;
            switch (compareList<T>(list[big], key, value)) {
                case 0: return big;
                case -1: return desc ? big - 1 : big + 1;
                case 1: return desc ? big + 1 : big;
            }
        }
        else if (small + 1 == big) {
            // 差一个的时候比较一下小的
            // switch (compareList<T>(list[big], key, value)) {
            //     case 0: return big;
            //     case -1: return desc ? small : (big == list.length ? big : big + 1);
            //     case 1: return desc ? big + 1 : small;
            // }

            // 这里比较一下，先从小的开始
            var sr = -2, br = -2;
            if (small >= 0) {
                sr = compareList<T>(list[small], key, value);
            }

            if (big < list.length) {
                br = compareList<T>(list[big], key, value);
            }
            if (desc) {
                if (sr != -2 && br != -2) {
                    if (sr == -1) {
                        return small - 1;
                    }
                    else if (sr == 0) {
                        return small;
                    }
                    else if (br == 0) {
                        return big;
                    }
                    else if (br == -1) {
                        // 这里 比小的大，但是比大的小
                        return small;
                    }
                    else {
                        return big;
                    }
                }
                else if (br != -2) {
                    // 这里小的是不存在的，就是说大的是第 0 位
                    if (br == -1) {
                        return small;
                    }
                    else {
                        return big;
                    }
                }
                else if (sr != -2) {
                    // 这里表示大的不存在，就是说小的是最后一个
                    if (sr == -1) {
                        return small - 1;
                    }
                    else {
                        return small;
                    }
                }
                else {
                    return -1;
                }
            }
            else {
                if (sr != -2 && br != -2) {
                    if (sr == 1) {
                        return small - 1;
                    }
                    else if (sr == 0) {
                        return small;
                    }
                    else if (br == 0) {
                        return big;
                    }
                    else if (br == 1) {
                        // 这里 比小的大，但是比大的小
                        return small;
                    }
                    else {
                        return big;
                    }
                }
                else if (br != -2) {
                    // 这里小的是不存在的，就是说大的是第 0 位
                    if (br == 1) {
                        return small;
                    }
                    else {
                        return big;
                    }
                }
                else if (sr != -2) {
                    // 这里表示大的不存在，就是说小的是最后一个
                    if (sr == 1) {
                        return small - 1;
                    }
                    else {
                        return small;
                    }
                }
                else {
                    return -1;
                }
            }
        }
        else {
            ret = compareList<T>(list[center], key, value);
        }

        if (desc) {
            switch (ret) {
                case 0:// 中间值是相等的数值的 当成 1处理
                    big = center;
                    break;
                case 1:
                    small = center;
                    break;
                case -1: big = center; break;
            }
        }
        else {
            switch (ret) {
                case 0: // 中间值是相等的数值的 当成 1处理
                    big = center;
                    break;
                case 1: big = center; break;
                case -1:
                    small = center;
                    break;
            }
        }
    }
}

function orderListInsert<T>(insertValue: T, list: Array<T>, key: string, value: any, desc: boolean = false) {
    // 先找一下位置
    var index = orderListFind<T>(list, key, value, desc);
    list.splice(index + 1, 0, insertValue);
}

function orderListRemove<T>(list: Array<T>, key: string, value: any, desc: boolean = false) {
    var index = orderListFind<T>(list, key, value, desc);
    for (var i = index; i < list.length; i++) {
        var rkInfo: any = list[i];
        if (rkInfo) {
            if (rkInfo[key] == value) {
                list.splice(i, 1);
            }
            else if ((rkInfo[key] > value && !desc) || (rkInfo[key] < value && desc)) {
                break;
            }
            else if (i != index) {
                console.log("err");
            }
        }

    }
}

export class TeMap<T>{
    _data: any = {};

    constructor(_data?: Object) {
        if (_data) {
            this._data = _data;
        }
    }

    has(key: string | number) {
        return this._data.hasOwnProperty(key.toString());
    }

    get(key: string | number) {
        return <T>this._data[key]
    }

    set(key: string | number, v: T) {
        this._data[key] = v;
    }

    get keys() {
        return Object.keys(this._data);
    }

    del(key: string | number) {
        var obj = this._data[key];
        if (obj && typeof obj == 'object' && obj['destory'] && typeof obj['destory'] == 'function') {
            obj['destory']();
        }

        delete this._data[key];
    }

    rand() {
        var keys = this.keys;
        var tid = keys[Math.floor(Math.random() * keys.length)];
        return this.get(tid);
    }

    clear() {
        this._data = {};
    }
}

/**
 * list 的方式实现map
 */
export class MapList<T> {
    _data: Array<T> = [];
    mkey: string;

    constructor(mkey: string) {
        this.mkey = mkey;
    }

    private _find_index(v: string) {
        var num = orderListFind<T>(this._data, this.mkey, v);
        if (num < 0 || num >= this._data.length) return -1;

        var rInfo: any = this._data[num];
        if (rInfo[this.mkey] != v) {
            return -1;
        }

        return num;
    }

    has(k: number | string) {
        if (this._find_index(k.toString()) >= 0) {
            return true;
        }

        return false;
    }

    at(idx: number) {
        return this._data[idx];
    }

    get(v: number | string) {
        v = v.toString();

        var num = this._find_index(v);
        return (num == -1) ? null : this._data[num];
    }

    set(k: number | string, v: T) {
        k = k.toString();
        var num = this._find_index(k);

        if (num >= 0) {
            // 找到了就使用
            this._data[num] = v;
        }
        else {
            // 没找到就要新增一个
            orderListInsert(v, this._data, this.mkey, k);
        }
    }

    remove(v: number | string) {
        v = v.toString();
        orderListRemove(this._data, this.mkey, v);
    }

    get length() {
        return this._data.length;
    }

    clear() {
        this._data = [];
    }
}

var showUpdate = false;

function promise_setInterval(callback: () => Promise<any>, time: number): { stop: () => void };
function promise_setInterval(name: string, callback: () => Promise<any>, time: number): { stop: () => void };
function promise_setInterval(...args: any[]) {
    let [name, callback, time] = args;
    if (typeof name != 'string') {
        name = '';
        callback = args[0];
        time = args[1];
    }

    let handle: NodeJS.Timeout;
    function update() {
        if (showUpdate) console.log((handle as any).name, callback.name, 'start')
        createPromise(callback()).finally(function () {
            if (showUpdate) console.log((handle as any).name, callback.name, 'finish')
            if (handle) {
                if (handle.refresh) {
                    handle.refresh();
                }
                else {
                    handle = setTimeout(update, time);
                }
            }
        })
    }

    handle = setTimeout(update, time);
    (handle as any)['name'] = name;

    return {
        stop: function () {
            clearTimeout(handle);
        }
    }

}

export class TeRandom {
    private _seed = 5;
    static DEFAULT_SEED = 6364;
    constructor(seed: number = Math.random()) {
        this._seed = seed;
    }

    public reset(seed: number) {
        this._seed = seed;
    }

    get seed() {
        return this._seed;
    }

    public random(min?: number, max?: number, msg: string = "") {
        max = (max == undefined) ? 1 : max;
        min = (min == undefined) ? 0 : min;

        this._seed = (this._seed * 9301 + 49297) % 233280;
        var rnd = this._seed / 233280.0;
        // console.log(gMain.iGameTime,this._seed,min,max,min + rnd * (max - min),msg);
        return min + rnd * (max - min);
    };

    public randInt(min?: number, max?: number, msg: string = "") {
        return Math.floor(this.random(min, max, msg));
    }
}


export class HashMap<T>{
    _data: { [key: string]: T[] } = {};

    constructor() {

    }

    get(key: string | number) {
        key = key.toString();
        return (<Array<T>>this._data[key]) || [];
    }

    add(key: string | number, v: T) {
        if (typeof key == 'number') {
            key = key.toString();
        }

        if (!this._data[key]) {
            this._data[key] = [];
        }

        this._data[key].push(v);
    }

    get keys() {
        return Object.keys(this._data);
    }

    set(key: string | number, v: Array<T>) {
        key = key.toString();
        this._data[key] = v;
    }

    clear() {
        this._data = {};
    }

    has(key: string | number, v?: T) {
        key = key.toString();
        if (this._data.hasOwnProperty(key)) {
            if (v && this.get(key).indexOf(v) < 0) return false;

            return true;
        }

        return false;
    }

    sort(fn: (a: T, b: T) => number) {
        for (var key in this._data) {
            var r = this._data[key] as Array<T>;
            r.sort(fn);
        }
    }
}

// 拷贝函数 是否包括函数拷贝
function func_copy<T>(obj: T | any, bFunc = false): T {
    var out: any;
    if (obj instanceof Array) {
        out = [];
        // 数组这里就
        for (let i = 0; i < obj.length; i++) {
            out[i] = func_copy(obj[i], bFunc);
        }
    }
    else if (obj instanceof Buffer) {
        // buffer 这里就不拷贝算了
        out = Buffer.from(obj);
    }
    else if (typeof obj == 'object') {
        out = {};
        for (var key in obj) {
            if (key == 'clone' || key == 'global') {
                continue;
            }
            if (typeof obj[key] == 'function' && !bFunc) {
                continue;
            }
            if (obj[key] == null) { out[key] = null; }
            else out[key] = func_copy(obj[key], false);
        }
    }
    else {
        out = obj;
    }

    return <T>out;
}


function getCallerFileNameAndLine() {
    function getException() {
        try {
            throw Error('');
        } catch (err) {
            return err;
        }
    }

    const err = getException();

    const stack = err.stack;
    const stackArr = stack.split('\n');
    let callerLogIndex = 0;
    for (let i = 0; i < stackArr.length; i++) {
        if (stackArr[i].indexOf('errorMessage') > 0 && i + 1 < stackArr.length) {
            callerLogIndex = i + 1;
            break;
        }
    }

    if (callerLogIndex !== 0) {
        const callerStackLine = stackArr[callerLogIndex] as string;
        let list = callerStackLine.split(/\(|\)/)
        if (list[1] && list[1].length > 0) {
            return list[1].replace(process.cwd(), '').replace(/\\/g,"/").replace('/','')
        }
        else {
            return callerStackLine.trim();
        }
    } else {
        return '';
    }
}

export var Util = {
    Sort: {
        orderListFind: orderListFind,
        orderListRemove: orderListRemove,
        orderListInsert: orderListInsert
    },
    Map: TeMap,
    MapList: MapList,
    setInterval: promise_setInterval,
    switchShowInterval: function (b: boolean) {
        showUpdate = b
    },
    Random: TeRandom,
    HashMap: HashMap,
    copy: func_copy,
    errorMessage: function (code: number, msg: string) {
        return { code: code, errMsg: msg, __line__: getCallerFileNameAndLine() }
    }
}
